[https://nvbugs/6487038][fix] Stop single-rank disagg errors from crashing all gen ranks - #16834
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Enterprise Run ID: 📒 Files selected for processing (7)
🚧 Files skipped from review as they are similar to previous changes (7)
WalkthroughThe change tolerates transient active-request overshoot, handles completed response threads and event-loop errors, adds regression tests, and updates disaggregated NIXL transfer settings. ChangesExecutor safeguards and transfer configuration
Estimated code review effort: 3 (Moderate) | ~20 minutes Suggested reviewers: 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
adad095 to
d290731
Compare
|
/bot run --post-merge --stage-list "GB200-16_GPUs-4_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE2-GPU8-GEN1-NODE2-GPU8-Post-Merge, GB300-20_GPUs-5_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE4-GPU16-Post-Merge, GB300-12_GPUs-3_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE2-GPU8-Post-Merge" |
|
PR_Github #61811 [ run ] triggered by Bot. Commit: |
|
PR_Github #61811 Bot args parsing error: CI requested by |
05f16c5 to
9727efc
Compare
|
/bot run --post-merge --stage-list "GB200-16_GPUs-4_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE2-GPU8-GEN1-NODE2-GPU8-Post-Merge, GB300-20_GPUs-5_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE4-GPU16-Post-Merge, GB300-12_GPUs-3_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE2-GPU8-Post-Merge" |
|
PR_Github #61826 [ run ] triggered by Bot. Commit: |
|
PR_Github #61826 Bot args parsing error: CI requested by |
|
Removed the "ci: post-merge approved" label because @niukuo could not be verified as an active member of NVIDIA/trt-llm-ci-approvers. Ask a member of that team to apply it. |
9727efc to
e14fc1b
Compare
8a400e1 to
ad17083
Compare
|
/bot run --add-multi-gpu-test --disable-fail-fast --extra-stage "GB300-20_GPUs-5_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE4-GPU16-Post-Merge-1, GB300-20_GPUs-5_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE4-GPU16-Post-Merge-2, GB300-20_GPUs-5_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE4-GPU16-Post-Merge-3, GB300-20_GPUs-5_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE4-GPU16-Post-Merge-4, GB300-12_GPUs-3_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE2-GPU8-Post-Merge-1, GB300-12_GPUs-3_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE2-GPU8-Post-Merge-2" |
|
PR_Github #63367 [ run ] triggered by Bot. Commit: |
|
PR_Github #63367 [ run ] completed with state
|
ad17083 to
ca0c515
Compare
|
/bot run --add-multi-gpu-test --disable-fail-fast --extra-stage "GB300-20_GPUs-5_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE4-GPU16-Post-Merge-1, GB300-20_GPUs-5_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE4-GPU16-Post-Merge-2, GB300-20_GPUs-5_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE4-GPU16-Post-Merge-3, GB300-20_GPUs-5_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE4-GPU16-Post-Merge-4, GB300-12_GPUs-3_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE2-GPU8-Post-Merge-1, GB300-12_GPUs-3_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE2-GPU8-Post-Merge-2" |
|
PR_Github #63438 [ run ] triggered by Bot. Commit: |
|
PR_Github #63438 [ run ] completed with state
|
brnguyen2
left a comment
There was a problem hiding this comment.
The two code changes look right and the start_thread rework is a clear improvement — the old path fell through to thread.start() and raised RuntimeError: threads can only be started once out of submit(), hiding the actual engine failure.
Two things before merge:
-
Scope and description. The title and NVBug are about ADP crash containment, but half the diff is perf-sanity config tuning (CPP→PYTHON transceiver migration, bounce buffer, KV transfer threads). If these were bundled to avoid paying the disagg CI cost three times, that's a fair reason to keep them in one PR — but then please (a) split them into logically separate commits (ADP assert fix /
start_threaderror surfacing / perf-sanity config tuning) so each can be bisected and reverted on its own, and (b) list all three in the PR description. Right now the description reads as a single ADP fix, so the transceiver migration — a behavior change on three CI perf cases — is invisible to anyone scanning the log or the PR. -
The 8k1k configs still run with the default 60s
kv_transfer_timeout_mswhile switching to the PYTHON transceiver — see inline. The 128k8k config in this same PR uses600000.
Also: neither new code path is really covered. The new test only asserts "doesn't raise", and the start_thread change has none — tests/unittest/executor/test_event_loop_error_broadcast.py already has stubs that would make a 10-line test cheap.
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
Thanks, both paths are pinned now.
Overshoot test — replaced the "doesn't raise" assertion with |
08e7af3 to
19999f5
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@tests/unittest/executor/test_event_loop_error_broadcast.py`:
- Around line 109-114: Add a regression test alongside the existing start_thread
tests that creates the worker via _make_worker(can_enqueue=False), invokes
start_thread(), and asserts thread.start() is not called. Keep the existing
enabled-enqueueing coverage unchanged.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: CHILL
Plan: Enterprise
Run ID: 2cf0a1d7-ea3f-4177-86c7-86d0de359332
📒 Files selected for processing (7)
tensorrt_llm/_torch/pyexecutor/py_executor.pytensorrt_llm/executor/worker.pytests/scripts/perf-sanity/disaggregated/gb300_deepseek-r1-fp4_128k8k_con256_ctx1_pp4_gen1_dep8_eplb0_mtp1_ccb-NIXL.yamltests/scripts/perf-sanity/disaggregated/gb300_deepseek-r1-fp4_8k1k_con4096_ctx1_dep4_gen1_dep16_eplb0_mtp1_ccb-NIXL.yamltests/scripts/perf-sanity/disaggregated/gb300_kimi-k25-thinking-fp4_8k1k_con4096_ctx1_dep4_gen1_dep16_eplb0_mtp0_ccb-NIXL.yamltests/unittest/_torch/executor/test_py_executor.pytests/unittest/executor/test_event_loop_error_broadcast.py
🚧 Files skipped from review as they are similar to previous changes (6)
- tensorrt_llm/_torch/pyexecutor/py_executor.py
- tests/scripts/perf-sanity/disaggregated/gb300_deepseek-r1-fp4_8k1k_con4096_ctx1_dep4_gen1_dep16_eplb0_mtp1_ccb-NIXL.yaml
- tensorrt_llm/executor/worker.py
- tests/scripts/perf-sanity/disaggregated/gb300_kimi-k25-thinking-fp4_8k1k_con4096_ctx1_dep4_gen1_dep16_eplb0_mtp0_ccb-NIXL.yaml
- tests/unittest/_torch/executor/test_py_executor.py
- tests/scripts/perf-sanity/disaggregated/gb300_deepseek-r1-fp4_128k8k_con256_ctx1_pp4_gen1_dep8_eplb0_mtp1_ccb-NIXL.yaml
19999f5 to
40dfed1
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
|
/bot run --add-multi-gpu-test --disable-fail-fast --extra-stage "GB300-20_GPUs-5_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE4-GPU16-Post-Merge-1, GB300-20_GPUs-5_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE4-GPU16-Post-Merge-2, GB300-20_GPUs-5_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE4-GPU16-Post-Merge-3, GB300-20_GPUs-5_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE4-GPU16-Post-Merge-4, GB300-12_GPUs-3_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE2-GPU8-Post-Merge-1, GB300-12_GPUs-3_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE2-GPU8-Post-Merge-2, GB300-4_GPUs-PyTorch-PerfSanity-Post-Merge-1, GB300-4_GPUs-PyTorch-PerfSanity-Post-Merge-2, GB300-4_GPUs-PyTorch-PerfSanity-Post-Merge-3" |
… transfer error _pad_attention_dp_dummy_request() asserted expected_num_active_requests >= len(active_requests). Requests whose KV transfer errored can linger in active_requests for a tick before cleanup drains them, so a single-rank disagg error tripped the assert and took down the generation loop on every attention-DP rank. The overshoot is transient and self-correcting, and the padding below keys on the schedulable count rather than the raw length, so warn once and continue. Signed-off-by: Xiaowei Shi <39303645+Shixiaowei02@users.noreply.github.com>
…m start_thread A ManagedThread that already ran cannot be restarted, so start_thread fell through to thread.start() and raised "threads can only be started once" out of submit(), hiding the engine failure that actually killed the thread. Report the stashed _event_loop_error instead. It is wrapped as RequestError(str(err)) from err rather than re-raised: submit() calls start() every time, and re-raising the same object appends a frame to its __traceback__ on each call. This also matches how base_worker.py reports its other submit-path failures. The post-shutdown path -- stop() setting stop_event, with no error stashed -- returns quietly. Signed-off-by: Xiaowei Shi <39303645+Shixiaowei02@users.noreply.github.com>
…r configs Behavior change on three CI perf cases, kept in this PR so the disagg CI cost is paid once: - All three migrate the cache transceiver from CPP to PYTHON. - The two 8k1k con4096 cases get kv_cache_bounce_size_mb=2048 and TRTLLM_KV_TRANSFER_NUM_THREADS=4. - All three now set kv_transfer_timeout_ms=600000. The 8k1k cases previously relied on the 60s default while switching transceiver, which is the exact bound NVBug 6487038 reports being exceeded; transfer time varies with the environment, so bound all three the same way. The 128k8k case deliberately keeps neither the bounce buffer nor the extra transfer threads, and says so inline. Measured A/B on GB300 (3 nodes, 12 GPUs, one run each): adding them moved total token throughput 13691 -> 12604 tok/s (-7.9%), benchmark duration 7812 -> 8486 s, mean TTFT 2114893 -> 2304223 ms. Both variants passed 768/768 requests. Single sample per arm, but every metric moved the same way, and the slower arm finishes 9513s against a 180min budget. Signed-off-by: Xiaowei Shi <39303645+Shixiaowei02@users.noreply.github.com>
40dfed1 to
6f99769
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
|
/bot run --add-multi-gpu-test --disable-fail-fast --extra-stage "GB300-20_GPUs-5_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE4-GPU16-Post-Merge-1, GB300-20_GPUs-5_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE4-GPU16-Post-Merge-2, GB300-20_GPUs-5_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE4-GPU16-Post-Merge-3, GB300-20_GPUs-5_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE4-GPU16-Post-Merge-4, GB300-12_GPUs-3_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE2-GPU8-Post-Merge-1, GB300-12_GPUs-3_Nodes-PyTorch-Disagg-PerfSanity-CTX1-NODE1-GPU4-GEN1-NODE2-GPU8-Post-Merge-2, GB300-4_GPUs-PyTorch-PerfSanity-Post-Merge-1, GB300-4_GPUs-PyTorch-PerfSanity-Post-Merge-2, GB300-4_GPUs-PyTorch-PerfSanity-Post-Merge-3" |
|
PR_Github #64454 [ run ] triggered by Bot. Commit: |
|
PR_Github #64454 [ run ] completed with state
|
Description
Three separate changes, one per commit so each can be bisected and reverted on its own. They
are in one PR so the disagg CI cost is paid once rather than three times.
1.
[fix]Do not crash every ADP rank on a disagg transfer error —py_executor.py_pad_attention_dp_dummy_request()assertedexpected_num_active_requests >= len(active_requests). Requests whose KV transfer errored can linger inactive_requestsfor atick before cleanup drains them, so a single-rank disagg error tripped the assert and took down
the generation loop on every attention-DP rank. The overshoot is transient and
self-correcting, and the padding keys on the schedulable count rather than the raw length, so
it now warns once and continues.
2.
[fix]Surface the engine event-loop error fromstart_thread—worker.pyA
ManagedThreadthat already ran cannot be restarted, sostart_threadfell through tothread.start()and raisedRuntimeError: threads can only be started onceout ofsubmit(),hiding the engine failure that actually killed the thread. It now reports the stashed
_event_loop_error, wrapped asRequestError(str(err)) from err—submit()callsstart()every time, and re-raising the same object would append a frame to its
__traceback__on eachcall. This also matches how
base_worker.pyreports its other submit-path failures. Thepost-shutdown path (
stop()setstop_event, no error stashed) returns quietly.3.
[chore]Tune gb300 disagg perf-sanity transfer configs — three YAMLsA behavior change on three CI perf cases, so calling it out explicitly:
CPPtoPYTHON;kv_cache_bounce_size_mb: 2048andTRTLLM_KV_TRANSFER_NUM_THREADS=4;kv_transfer_timeout_ms: 600000. The 8k1k cases previously relied on the60 s default while switching transceiver, which is the exact bound NVBug 6487038 reports being
exceeded; transfer time varies with the environment, so all three are bounded the same way.
The 128k8k case deliberately gets neither the bounce buffer nor the extra transfer threads, and
says so inline, because measurement says they hurt this shape.
Test Coverage
Unit tests, all CPU-only:
tests/unittest/executor/test_event_loop_error_broadcast.py::TestStartThreadAfterExit(new):engine error →
RequestErrorwith__cause__intact and no restart; repeated calls → distinctexception objects, guarding the
__traceback__accumulation;_event_loop_error is None→quiet return; fresh thread → still starts.
tests/unittest/_torch/executor/test_py_executor.py: the overshoot test now pins the outcome(
add_dummy_calls == [],active_requestsunchanged) instead of only asserting no raise, andtest_pad_dummy_added_when_overshoot_has_no_schedulable_requestscovers the branch thatmatters — overshoot with zero schedulable requests must still add the pad dummy.
A/B for the 128k8k transfer config, GB300, 3 nodes / 12 GPUs,
disagg-e2e, one run per arm:One sample per arm on a shared cluster, so the exact percentages are soft — but every metric
moves the same way, and the slower arm finishes in 9513 s against the case's 180 min budget,
which is why the tuning is not applied there.
Dev Engineer Review
RequestErrorand avoid invalid restarts.600000ms timeouts to the 8k1k configurations.QA Engineer Review
test_pad_dummy_tolerates_active_request_overshoot().test_pad_dummy_added_when_overshoot_has_no_schedulable_requests().test_surfaces_engine_error_as_request_error().test_repeated_calls_do_not_accumulate_traceback().test_post_shutdown_exit_returns_quietly().test_fresh_thread_is_started().test_does_not_start_when_enqueueing_is_disabled().test-db/orqa/entries were provided for these tests.